/*
 *  gbase.h
 *  Description: base for GIGIONE,
 *  including system porting, socket, string, gigi object, 
 *  Created: 2009.10.26
 *  Version: 1.0
 */

#ifndef __GBASE_H__
#define __GBASE_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "gigione.h"

/* #define GIGI_DEBUG */

/**********************************
 * porting for Windows and Linux  *
 **********************************/

#ifdef WINDOWS
  #include <winsock2.h>  
  #include <windows.h>

  #define FD_SOCKET                  SOCKET

  #define FD_SOCKET_STARTUP()        \
          { struct WSAData wsa; WSAStartup (MAKEWORD (2, 2), &wsa); }

  #define FD_SOCKET_CLEANUP()        { WSACleanup (); }

  #define THREAD_T                   HANDLE
  #define THREAD_NULL                NULL
  #define THREAD_INIT(h)             h=THREAD_NULL

  #define THREAD_BEGIN(h,f,arg,id)   (h=CreateThread (0, 0,\
          (LPTHREAD_START_ROUTINE)f, arg, 0, ( (DWORD*)& (id))))

  #define THREAD_ID()    GetCurrentThreadId() 
  #define THREAD_JOIN(h) (WaitForSingleObject (h, INFINITE) == WAIT_OBJECT_0)
  #define THREAD_CLOSE   WIN_CLOSE_HANDLE

  #define PROCESS_ID()   GetCurrentProcessId()

  #define MUTEX_T           CRITICAL_SECTION 
  #define MUTEX_INIT(cs)    InitializeCriticalSection (&cs)
  #define MUTEX_FREE(cs)    DeleteCriticalSection (&cs)
  #define MUTEX_LOCK(cs)    EnterCriticalSection (&cs)
  #define MUTEX_UNLOCK(cs)  LeaveCriticalSection (&cs)

  #define WIN_CLOSE_HANDLE(v)  \
          do { if (v) { CloseHandle(v); (v) = NULL;}  } while (0)

  #define WIN_CLOSE_FILE_HANDLE(v)  \
          do { if (v != INVALID_HANDLE_VALUE) { \
              CloseHandle(v); (v) = INVALID_HANDLE_VALUE; } } while (0)

  #define GIGI_SLEEP(x)   Sleep (x)
  #define GIGI_MAX_PATH   MAX_PATH

  typedef int socklen_t;

#else
  #include <stdlib.h>
  #include <string.h>
  #include <sys/socket.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
  #include <sys/select.h>
  #include <sys/time.h>
  #include <netdb.h>
  #include <fcntl.h>
  #include <errno.h>
  #include <pthread.h>

  #define INVALID_SOCKET            -1
  #define closesocket(s)            close (s)
  #define FD_SOCKET                 int
  #define FD_SOCKET_STARTUP()       do {} while(0)
  #define FD_SOCKET_CLEANUP()       do {} while(0)

  #define  THREAD_T          pthread_t
  #define THREAD_NULL          0
  #define THREAD_INIT(h)        (h) = THREAD_NULL
  #define THREAD_BEGIN(h,f,arg,id)  \
          do { pthread_create (&(h), NULL, f, arg);\
          id = (int)h; } while (0)

  #define THREAD_ID()               pthread_self()
  #define THREAD_JOIN(h)            pthread_join (h, NULL)
  #define THREAD_CLOSE(h)           h=THREAD_NULL
  #define PROCESS_ID()              getpid()

  #define MUTEX_T                   pthread_mutex_t
  #define MUTEX_INIT(cs)            pthread_mutex_init (&cs, NULL)
  #define MUTEX_FREE(cs)            pthread_mutex_destroy (&cs)
  #define MUTEX_LOCK(cs)            pthread_mutex_lock (&cs)
  #define MUTEX_UNLOCK(cs)          pthread_mutex_unlock (&cs)
  
  #define __w64 

  #define GIGI_SLEEP(x)              sleep( x<1000 ? 1 : x/1000)
  #define GIGI_MAX_PATH              PATH_MAX
#endif

#include <time.h>
#include <stdio.h>

/* Basic operation 
 *    Time , Memory
 */
typedef GIGI_UINT64             GIGI_TIME;

#define NEW_OBJECT(n)           (n*)malloc (sizeof (n))

#define FREE_AND_INIT(p)        \
        do { if (p) { free (p); (p) = NULL; }; } while(0)

#define LOG_OUT_OF_MEMORY()     \
        do { GIGI_ERROR ( "out of memory", 0); } while (0)

#define STATIC_ALLOC(ty,buf,sz) \
        ( ((sz) <= sizeof(buf)) ? (ty)buf : (ty) malloc ( sz ) )

#define STATIC_FREE(v,buf)      do { \
  if ( (void *)(v) != (void *)(buf) && (v) != NULL) free (v); } while (0)

#define CER(n)         do { if ( (n) < 0 ) goto Cleanup;} while(0)

#define ERR_RETURN(n)  do { if ( (n) < 0 ) return (n);  } while(0)

#define GIGI_E_INTERNAL_ERROR  (-THIS_FILE_ERROR_ID-__LINE__)

#ifdef GIGI_DEBUG
  #define GIGI_PERF_BEGIN_EX(perf)      GIGI_TIME perf = gigi_now ();
  #define GIGI_PERF_CHECK_EX(perf, n)   { int a = gigi_time_diff (perf); \
          if ( a > n ) { GIGI_WARNING ("Perf Issue at %s (time=%d)\n",\
          __FUNCTION__, a); } }

#else

  #define GIGI_PERF_BEGIN_EX(perf)
  #define GIGI_PERF_CHECK_EX(perf, n)

#endif

#define GIGI_PERF_BEGIN       GIGI_PERF_BEGIN_EX(perfTime)
#define GIGI_PERF_CHECK(n)    GIGI_PERF_CHECK_EX(perfTime, n)

#ifndef WINDOWS
  #define __FUNCTION__ __func__
#endif

#define GIGI_CONN_STRING   "GIGI_CONN_STRING"
#define GIGI_TOKEN_STRING  "GIGI_TOKEN_STRING"

/* function gigi_now
 * return current time
 */
GIGI_TIME gigi_now ();


/* function gigi_calc_tps
 * return tps
 * start_time(in) :
 * count(in)      :
 */
int gigi_calc_tps (GIGI_TIME start_time, int count);


/* function gigi_time_diff
 * return time diff in mill seconds
 * start_time(in)  : base time
 */
#define gigi_time_diff(start_time) \
        ( (int) ( (gigi_now () - (start_time))/10000))


/* gigi_log --- print the log
 *
 * file(in) --- file name of source file
 * line(in) --- the line of the source code
 * ty(in)   --- type of the log
 * fmt(in)  --- format of the log string
 * ...(in)  --- 
 */
void gigi_log (GIGI_LPCSTR file, int line, GIGI_LPCSTR func,
               GIGI_LPCSTR ty, GIGI_LPCSTR fmt,...);

#define GIGI_LOG(ty,fmt,...)  \
  gigi_log (__FILE__, __LINE__, __FUNCTION__, ty, fmt, __VA_ARGS__)

#define GIGI_ERROR(fmt,...)   GIGI_LOG ("Err", fmt, __VA_ARGS__)
#define GIGI_TRACE(fmt,...)   GIGI_LOG ("Inf", fmt, __VA_ARGS__)
#define GIGI_WARNING(fmt,...) GIGI_LOG ("Wrn", fmt, __VA_ARGS__)



/******************************************
 *            Byte Order                  *
 ******************************************/

#define BO_I386     1
#define BO_HP       2
#define BO_PPC      3
#define BO_TY_16    unsigned short
#define BO_TY_32    unsigned
#define BO_TY_64    GIGI_UINT32

#define BO_CHG(bits,from,to,n)  BO_CHG_##bits##_##from##_##to(n)

#define BO_CHGV(bits,from,to,n) \
        *((BO_TY_##bits*)n)=BO_CHG(bits,from,to,*((BO_TY_##bits*)n))

#define BO_CHGV_AUTO(bits,bo,v) do { if (bo==BO_MY) { } \
  else if (bo==BO_1) BO_CHGV_1(bits,v); \
  else if (bo==BO_2) BO_CHGV_2(bits,v); } while(0)

#define BO_htonl64(n) \
        (((GIGI_UINT64)( htonl ( (u_long)( (n) & 0xFFFFFFFF ) ) ) << 32) | \
        (GIGI_UINT64)( htonl ( (u_long)( (n) >> 32 ) ) ) )

#define BO_CHG_16_I386_HP(n)   htons ( (u_short)(n) )
#define BO_CHG_32_I386_HP(n)   (n)
#define BO_CHG_64_I386_HP(n)   (n)

#define BO_CHG_16_I386_PPC(n)  htons ( (u_short)(n) ) 
#define BO_CHG_32_I386_PPC(n)  htonl ( (u_long)(n) )
#define BO_CHG_64_I386_PPC(n)  BO_htonl64(n)

#define BO_CHG_16_HP_PPC(n)    htons ( (u_short)(n) )
#define BO_CHG_32_HP_PPC(n)    htonl ( (u_long)(n) )
#define BO_CHG_64_HP_PPC(n)    BO_htonl64(n)

/*
 * BO_MY should be configurated in config.h file
 */
#ifndef BO_MY
  #define BO_MY BO_I386
#endif

#define BO_1   ((BO_MY % 3) + 1)
#define BO_2   (((BO_MY + 1) % 3 ) + 1)

#if BO_MY==BO_I386
  #define BO_CHG_1(bits,n)    BO_CHG(bits,I386,HP,n)
  #define BO_CHG_2(bits,n)    BO_CHG(bits,I386,PPC,n)
  #define BO_CHGV_1(bits,v)   BO_CHGV(bits,I386,HP,v)
  #define BO_CHGV_2(bits,v)   BO_CHGV(bits,I386,PPC,v)
#endif 

#if BO_MY==BO_HP
  #define BO_CHG_1(bits,n)    BO_CHG(bits,HP,PPC,n)
  #define BO_CHG_2(bits,n)    BO_CHG(bits,I386,HP,n)
  #define BO_CHGV_1(bits,v)   BO_CHGV(bits,HP,PPC,v)
  #define BO_CHGV_2(bits,v)   BO_CHGV(bits,I386,HP,v)
#endif

#if BO_MY==BO_PPC
  #define BO_CHG_1(bits,n)    BO_CHG(bits,I386,PPC,n)
  #define BO_CHG_2(bits,n)    BO_CHG(bits,HP,PPC,n)
  #define BO_CHGV_1(bits,v)   BO_CHGV(bits,I386,PPC,v)
  #define BO_CHGV_2(bits,v)   BO_CHGV(bits,HP,PPC,v)
#endif

/* Byte Order
 *
 * System        :  16         32         64
 * Network bytes :  1,2      1,2,3,4      1,2,3,4,5,6,7,8
 * i386          :  2,1      4,3,2,1      8,7,6,5,4,3,2,1
 * hp            :  1,2      3,4,1,2      7,8,5,6,3,4,1,2  ??
 * Power PC      : same as network bytes

 * #define GIGI_BYTE_ORDER_I386
 * #define GIGI_BYTE_ORDER_POWER_PC
 * #define GIGI_BYTE_ORDER_HP
 */

#define GIGI_BIG_ENDIAN
#ifdef  GIGI_BIG_ENDIAN

  #define  HOST_BYTE_INT64(s)   \
    ( ((GIGI_UINT64)( htonl ( (u_long)( (s) & 0xFFFFFFFF ) ) ) << 32) \
                    | htonl( (u_long)( (s) >> 32 ) ) )

  #define  HOST_BYTE_INT32(s)   htonl ( (u_long)s)
  #define  HOST_BYTE_INT16(s)   htons ( (u_short)s)

  #define  BYTE_CHANGE_INT64(v) ( (v)=HOST_BYTE_INT64 (v))
  #define  BYTE_CHANGE_INT32(v) ( (v)=HOST_BYTE_INT32 (v))
  #define  BYTE_CHANGE_INT16(v) ( (v)=HOST_BYTE_INT16 (v))

#else
  #define  HOST_BYTE_INT64(s)   (s)
  #define  HOST_BYTE_INT32(s)   (s)
  #define  HOST_BYTE_INT16(s)   (s)

  #define  BYTE_CHANGE_INT64(v) do{}while(0)
  #define  BYTE_CHANGE_INT32(v) do{}while(0)
  #define  BYTE_CHANGE_INT16(v) do{}while(0)
#endif

#define GIGI_PUT_INT64(buf,i)  \
  (*(GIGI_UINT64  *)(buf)) = HOST_BYTE_INT64 ( ((GIGI_UINT64)(i)) )

#define GIGI_PUT_INT32(buf,i)  \
  (*(unsigned int  *)(buf)) = HOST_BYTE_INT32 ( ((unsigned)(i)) )

#define GIGI_PUT_INT16(buf,i)  \
  (*(unsigned short*)(buf)) = HOST_BYTE_INT16 ( ((unsigned short)(i)) )

#define GIGI_GET_INT64(buf,i)  i = HOST_BYTE_INT64 (*(GIGI_UINT64  *)(buf))
#define GIGI_GET_INT32(buf,i)  i = HOST_BYTE_INT32 (*(unsigned int  *)(buf))
#define GIGI_GET_INT16(buf,i)  i = HOST_BYTE_INT16 (*(unsigned short*)(buf))

#define GIGI_GET_ID     GIGI_GET_INT64
#define GIGI_PUT_ID     GIGI_PUT_INT64
#define BYTE_CHANGE_ID  BYTE_CHANGE_INT64

#define GIGI_FIO_GET_BO   1


/**********************************
 *         List operation         *
 **********************************/

#define LIST_PUSH_FRONT(h, n)   ( (n)->_next = (h), (h) = (n))
#define LIST_POP_FRONT(h, n)    do { n = h; (h) = (h)->_next; } while (0)
#define LIST_NEXT(s)            ( (s) = (s)->_next)
#define LIST_PP_NEXT(s)         ( (s) = & ( (*s)->_next))
#define LIST_PP_DEL(r)          (* (r) = (* (r))->_next)


/**********************************
 *         Ring operation         *
 **********************************/

struct ring_root_t
{
  struct ring_root_t* _next;
  struct ring_root_t* _prev;
};

typedef struct ring_root_t ring_root, *ring_root_ptr;

struct ring_index_t
{
  struct ring_index_t* _next;
  struct ring_index_t* _prev;
  void * _host;
};

typedef struct ring_index_t ring_index;

#define RING_PTR(r)           ( (ring_root*)(r) )
#define RING_INIT(r)          ( (r)->_next = (r)->_prev = (r))

#define RING_PUSH_FRONT(r,n)                        \
       ( RING_PTR(n)->_next = RING_PTR(r)->_next,   \
         RING_PTR(n)->_prev = RING_PTR(r),          \
         RING_PTR(r)->_next->_prev = RING_PTR(n),   \
         RING_PTR(r)->_next = RING_PTR(n))

#define RING_PUSH_BACK(r,n)                         \
       ( RING_PTR(n)->_next = RING_PTR(r),          \
         RING_PTR(n)->_prev = RING_PTR(r)->_prev,   \
         RING_PTR(r)->_prev->_next = RING_PTR(n),   \
         RING_PTR(r)->_prev = RING_PTR(n) )

#define RING_POP_FRONT(r)                                \
       ( RING_PTR(r)->_next->_next->_prev = RING_PTR(r), \
         RING_PTR(r)->_next = RING_PTR(r)->_next->_next)

#define RING_DEL(n)                                            \
        do { ring_root_ptr p0 = (ring_root_ptr) ( (n)->_prev); \
        RING_POP_FRONT (p0); } while (0)

#define RING_IS_EMPTY(r)      ( (r)->_next == (r))
#define RING_BEGIN(r,ty)      ( (ty)( (r)->_next) )
#define RING_END(r,ty)        ( (ty)( r ) )
#define RING_NEXT              LIST_NEXT


/************************************
 *        Socket operation          *
 ************************************/

#define  FD_CLOSE_SOCKET(fd)                               \
         do{ if (FD_SOCKET_VALID (fd)) { closesocket (fd); \
         (fd) = INVALID_SOCKET; } } while (0)

#define  FD_SOCKET_VALID(fd)        ( (fd) != INVALID_SOCKET )

#define  FD_FDSET(fd,set,nMin,nMax)                        \
         do { FD_SET (fd,set);                             \
         if (fd<nMin) nMin = fd;                           \
         if (fd>nMax) nMax=fd; } while (0)

/* function fd_tcp_bind
 * return socket handle
 * port(in/out)  : 
 */
FD_SOCKET fd_tcp_bind (int * port);

/* function fd_connect
 * return socket handle
 * ip(in)   :
 * port(in) :
 * timeout(in) : in mill seconds
 */
FD_SOCKET fd_connect (unsigned int ip, int port, int timeout);

/* function fd_getip
 * return ip
 * hostname(in)  :
 * port(in/out)  : in is default port, out is the real port
 * isUniformAddr(out)  : is this ip is unique ip address
 */
unsigned fd_getip (GIGI_LPCSTR hostname, int * port, int * isUniformAddr);

/* function fd_gethostid
 *
 */
unsigned fd_gethostid (int * isAddr);

/* function fd_get_port
 * 
 */
int fd_get_port (FD_SOCKET fd);

/* function fd_socket_pipe
 * return 0 if success otherwise return error code
 * helpFD (in)  : a socket already bind to a port, 
 *                used to gen in/out socket pair,
 *                if helpFD == INVALID_SOCKET then a tempoary will be created.
 * in/out (out)  : socket handle pair, which connect to each other
 */
int fd_socket_pipe (FD_SOCKET helpFD, FD_SOCKET * in, FD_SOCKET * out);

/* function fd_send
 * 
 */
int fd_send (FD_SOCKET fd, const void * buff, int size, int timeout);

/* function fd_recv
 * 
 */
int fd_recv (FD_SOCKET fd, void * buff, int size, int timeout);



/****************************************
 *       String Operation               *
 ****************************************/

/* function strings_split
 *          split a string to string array
 * return the string list
 * s(in)       :
 * delims(in)  : delims char
 */
GIGI_LPSTR * strings_split (GIGI_LPCSTR s, int delims);

/* function 
 *
 */
char * strings_join (char * buf, int buf_size, GIGI_LPCSTR * argv, 
                     int argc, char  delims, char ch);

/* function strings_count
 * return  : count of strings
 * l(in)   : strings
 */
int strings_count (GIGI_LPCSTR * l);

/* function string_hash
 * return : hash of string
 * n(in)  : initial hash value
 * p(in)  : string buffer
 * len(in): length of string buffer
 */
unsigned string_hash (unsigned n, const void * p, unsigned len);

/* function string_parser_as_ini_line
 * type(out) 1 for comment, 2 for section, 3 for key-value, 0 for error
 */
char* string_parser_as_ini_line (char * line,int * type);

/* function conn_string_parser
 * return string list
 * conn_string(in)  : conn string
 * delims(in)       : delims charactar
 */
GIGI_LPSTR * conn_string_parser (GIGI_LPCSTR conn_string, int delims);

/* function conn_string_get
 *
 */
GIGI_LPCSTR conn_string_get (GIGI_LPSTR * conn, GIGI_LPCSTR key,
                             GIGI_LPCSTR def);

/* function conn_string_get_list
 *
 */
int conn_string_get_list (GIGI_LPSTR * conn, GIGI_LPCSTR key,
                          GIGI_LPCSTR * list, int count);

/* function conn_string_get_int
 *
 */
int conn_string_get_int (GIGI_LPSTR * conn, GIGI_LPCSTR key,
                         int def, int nMin, int nMax);

/* function conn_string_get_ip
 *
 */
unsigned conn_string_get_ip (GIGI_LPSTR * conn, GIGI_LPCSTR key,
                             GIGI_LPCSTR def);

/* function node_id_to_sting
 *
 */
char * node_id_to_string (GIGI_ID id, char * s);

/* function service_id_to_string
 *
 */
char * service_id_to_string (GIGI_ID id, char * s);


/**************************************
 *       Stream Operation             *
 **************************************/

GIGI_STREAM gigi_fopen (GIGI_LPCSTR filename, GIGI_LPCSTR type);

extern struct gigi_stream_vt g_file_stream_vt;

struct gigi_stream_file_t
{
  struct gigi_stream_vt * vt;
#ifdef WINDOWS
  HANDLE fp;
#else
  FILE* fp;
#endif
};


#if defined(SOLARIS)
  FILE* fopen_solaris (GIGI_LPCSTR filename, GIGI_LPCSTR type);
#endif

#if defined(SOLARIS)
  #define g_fopen fopen_solaris
#elif defined(HPUX)
  #if _LFS64_LARGEFILE == 1
  #define g_fopen fopen64
  #else
  #define g_fopen fopen
  #endif
#elif defined(AIX) || (defined(I386) && defined(LINUX))
  #define g_fopen fopen64
#else /* NT, ALPHA_OSF, and the others */
  #define g_fopen fopen
#endif


/*
 * socket stream 
 *
 */
GIGI_STREAM fd_open (FD_SOCKET fd);
extern struct gigi_stream_vt g_socket_stream_vt;

struct fd_stream_t
{
  struct gigi_stream_vt* _vt;
  FD_SOCKET _fd;
  int _timeout;
  int _bo;
};

#define fd_stream_init(v) \
  do { (v)._vt = &g_socket_stream_vt; (v)._timeout=-1; \
       (v)._fd=INVALID_SOCKET; (v)._bo=0; } while (0)

#ifdef __cplusplus
}
#endif

#endif

